package com.cybertron.ironhide.spider;

import java.io.File;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.eweb4j.util.xml.BeanXMLUtil;
import org.eweb4j.util.xml.XMLReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import com.cybertron.ironhide.spider.plugin.PluginManager;
import com.cybertron.ironhide.spider.task.Task;
import com.cybertron.ironhide.spider.task.TaskQueue;
import com.cybertron.ironhide.spider.xml.Plugin;
import com.cybertron.ironhide.spider.xml.Plugins;
import com.cybertron.ironhide.spider.xml.Site;

public class SiteManager implements InitializingBean {

	private SpiderListener listener;

	private Map<String, Site> sites;

	public ExecutorService pool;

	private Integer corePoolSize;

	private Integer maximumPoolSize;

	private Integer taskSize = 10;

	public TaskQueue queue;

	public SiteManager() {
		super();
	}

	private void initSites() throws Exception {
		File siteFolder = new File(Settings.WEB_SITE_XML_FOLDER);
		if (!siteFolder.exists()) {
			throw new Exception("can not found WebSites folder -> "
					+ siteFolder.getAbsolutePath());
		}

		if (!siteFolder.isDirectory()) {
			throw new Exception("WebSites -> " + siteFolder.getAbsolutePath()
					+ " must be folder !");
		}

		File[] files = siteFolder.listFiles();

		sites = new LinkedHashMap<String, Site>(files.length);
		for (File file : files) {
			if (!file.exists()) {
				continue;
			}
			if (!file.isFile()) {
				continue;
			}
			if (file.isDirectory()) {
				continue;
			}
			if (!file.getName().endsWith(".xml")) {
				continue;
			}
			XMLReader reader = BeanXMLUtil.getBeanXMLReader(file);
			reader.setBeanName("site");
			reader.setClass("site", Site.class);
			Site site = reader.readOne();
			if (site == null)
				throw new Exception("site xml file error -> "
						+ file.getAbsolutePath());
			if ("1".equals(site.getEnable())) {
				if (site.getTargets() == null
						|| site.getTargets().getTarget() == null) {
					throw new Exception("site target required");
				}
				if (site.getName() == null
						|| site.getName().trim().length() == 0) {
					throw new Exception("site name required");
				}
				// 初始化网站目标Model计数器
				site.counter = new Counter();
				sites.put(site.getHost(), site);
				listener.onInfo(Thread.currentThread(), null, "site.xml file->"
						+ file.getAbsolutePath() + " loading... -> ok");
			}
		}
	}

	private void loadPlugins() throws Exception {
		File pluginFile = new File(Settings.WEB_PLUGIN_XML_FILE);
		if (!pluginFile.exists()) {
			return;
		}
		XMLReader reader = BeanXMLUtil.getBeanXMLReader(pluginFile);
		reader.setBeanName("plugins");
		reader.setClass("plugins", Plugins.class);
		Plugins pluginsBean = reader.readOne();
		if (pluginsBean == null) {
			throw new Exception("plugins xml file error -> "
					+ pluginFile.getAbsolutePath());
		}
		listener.onInfo(Thread.currentThread(), null, "load plugin file---->"
				+ pluginFile.getAbsolutePath() + " loading... -> ok");

		// ---------------------插件初始化开始----------------------------
		listener.onInfo(Thread.currentThread(), null,
				"plugins loading begin...");

		List<Plugin> plugins = pluginsBean.getPlugin();
		PluginManager pluginManager = new PluginManager();
		pluginManager.loadPluginConf(plugins, listener);
		for (String key : sites.keySet()) {
			Site site = sites.get(key);
			site.init(this, pluginManager, listener);
		}
	}

	public void setListener(SpiderListener listener) {
		this.listener = listener;
	}

	public SpiderListener getListener() {
		return listener;
	}

	public Map<String, Site> getSites() {
		return sites;
	}

	private void initPool() {
		RejectedExecutionHandler rejectedHandler = new RejectedExecutionHandler() {
			public void rejectedExecution(Runnable r,
					ThreadPoolExecutor executor) {
				// 拿到被弹出来的爬虫引用
				Spider spider = (Spider) r;
				try {
					// 将该爬虫的任务 task 放回队列
					spider.pushTask(Arrays.asList(spider.task));
					String info = "repush the task->" + spider.task
							+ " to the Queue.";
					spider.listener.onError(Thread.currentThread(),
							spider.task, info, new Exception(info));
				} catch (Exception e) {
					String err = "could not repush the task to the Queue. cause -> "
							+ e.toString();
					spider.listener.onError(Thread.currentThread(),
							spider.task, err, e);
				}
			}
		};
		pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 60L,
				TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
				rejectedHandler);
		queue = new TaskQueue();
		queue.init();
		for (int i = 0; i < taskSize; i++) {
			pool.execute(new TaskExector(pool, this, listener));
		}

	}

	public Task pollTask() throws Exception {
		return queue.pollTask();
	}

	public void setCorePoolSize(Integer corePoolSize) {
		this.corePoolSize = corePoolSize;
	}

	public void setMaximumPoolSize(Integer maximumPoolSize) {
		this.maximumPoolSize = maximumPoolSize;
	}

	public ExecutorService getPool() {
		return pool;
	}

	public void setTaskSize(Integer taskSize) {
		this.taskSize = taskSize;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(listener, "listener could't be null");
		try {
			initPool();
			initSites();
			loadPlugins();
		} catch (Throwable e) {
			e.printStackTrace();
			listener.onInfo(Thread.currentThread(), null,
					"Spiderman init error.");
			listener.onError(Thread.currentThread(), null,
					"Spiderman init error.", e);
		}
	}

}
